﻿@page "/identity/users"
@using ActualLab.Fusion
@using CleanArchitecture.Blazor.Application.Features.Identity.Mappers
@using CleanArchitecture.Blazor.Application.Features.Fusion
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.UserActivation
@using CleanArchitecture.Blazor.Infrastructure.Persistence
@using CleanArchitecture.Blazor.Infrastructure.Services.MultiTenant
@using CleanArchitecture.Blazor.Server.UI.Components.UtcToLocalTime
@using Microsoft.AspNetCore.WebUtilities
@using CleanArchitecture.Blazor.Domain.Identity
@using CleanArchitecture.Blazor.Application.Features.Identity.DTOs
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.ResetPassword
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.SendWelcome
@using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity
@using CleanArchitecture.Blazor.Application.Common.Interfaces.MultiTenant
@using BlazorDownloadFile
@using CleanArchitecture.Blazor.Server.UI.Pages.Identity.Roles.Components
@using CleanArchitecture.Blazor.Server.UI.Pages.Identity.Users.Components
@using System.Linq.Expressions
@using CleanArchitecture.Blazor.Infrastructure.Constants.Role
@using CleanArchitecture.Blazor.Infrastructure.Constants.ClaimTypes
@using System.Security.Claims
@using System.Globalization
@using ZiggyCreatures.Caching.Fusion

@attribute [Authorize(Policy = Permissions.Users.View)]
@inherits OwningComponentBase<ApplicationDbContext>
@implements IDisposable

@inject IOnlineUserTracker OnlineUserTracker
@inject IBlazorDownloadFileService BlazorDownloadFileService
@inject IUserService UserService
@inject ITenantService TenantsService
@inject IFusionCache FusionCache
@inject IExcelService ExcelService
@inject IMailService MailService
@inject IStringLocalizer<Users> L
@inject ILogger<Users> Logger

<PageTitle>@Title</PageTitle>

<MudDataGrid @ref="_table"
             FixedHeader="true"
             FixedFooter="false"
             Height="calc(100vh - 300px)"
             @bind-RowsPerPage="_defaultPageSize"
             Hover="true"
             SelectOnRowClick="false"
             MultiSelection="true"
             @bind-SelectedItems="_selectedItems"
             Loading="@_loading"
             ServerData="@(ServerReload)">
    <ToolBarContent>
        <MudStack Row Spacing="0" Class="flex-grow-1" Justify="Justify.SpaceBetween">
            <MudStack Row AlignItems="AlignItems.Start">
                <MudIcon Icon="@Icons.Material.Filled.SwitchAccount" Size="Size.Large" />
                <MudStack Spacing="0">
                    <MudText Typo="Typo.subtitle2" Class="mb-2">@Title</MudText>
                    <MudSelect T="string" Style="min-width:120px" ValueChanged="OnChangedListView" Value="@_selectedTenantId" Dense="true" Label="@L["List View"]">
                        <MudSelectItem T="string" Value="@(" ")">@L["ALL"]</MudSelectItem>
                        @foreach (var item in TenantsService.DataSource)
                        {
                            <MudSelectItem T="string" Value="@item.Id">@item.Name</MudSelectItem>
                        }
                    </MudSelect>
                </MudStack>
            </MudStack>
            <MudStack Spacing="0" AlignItems="AlignItems.End">
                <MudToolBar Dense WrapContent="true" Class="py-1 px-0">
                    <MudButton 
                               Disabled="@_loading"
                               OnClick="@(() => OnRefresh())"
                               StartIcon="@Icons.Material.Outlined.Refresh">
                        @ConstantString.Refresh
                    </MudButton>
                    @if (_canCreate)
                    {
                        <MudButton 
                                   StartIcon="@Icons.Material.Outlined.Add"
                                   OnClick="OnCreate">
                            @ConstantString.New
                        </MudButton>
                    }
                    <MudMenu  TransformOrigin="Origin.BottomRight" AnchorOrigin="Origin.BottomRight" EndIcon="@Icons.Material.Filled.MoreVert" Label="@ConstantString.More">
                        @if (_canDelete)
                        {
                            <MudMenuItem Disabled="@(!(_selectedItems.Count > 0))"
                                         OnClick="OnDeleteChecked">
                                @ConstantString.Delete
                            </MudMenuItem>
                        }
                        @if (_canExport)
                        {
                            <MudMenuItem Disabled="@_exporting"
                                         OnClick="OnExport">
                                @ConstantString.Export
                            </MudMenuItem>
                        }
                        @if (_canImport)
                        {
                            <MudMenuItem>
                                <MudFileUpload T="IBrowserFile" FilesChanged="OnImportData" Accept=".xlsx">
                                    <ActivatorContent>
                                        <MudButton Class="pa-0 ma-0" Style="font-weight:400;text-transform:none;"
                                                   Variant="Variant.Text"
                                                   Disabled="@_uploading">
                                            @ConstantString.Import
                                        </MudButton>
                                    </ActivatorContent>
                                </MudFileUpload>
                            </MudMenuItem>
                        }
                    </MudMenu>
                </MudToolBar>
                <MudStack Row Spacing="1">
                    @if (_canSearch)
                    {
                        <MudSelect T="string" Placeholder="@L["Search by role name"]" Value="@_searchRole" Clearable="true" ValueChanged="@(OnSearchRole)" Style="width:150px">
                            @foreach (var str in _roles)
                            {
                                <MudSelectItem Value="@str">@str</MudSelectItem>
                            }
                        </MudSelect>
                        <MudTextField T="string" Immediate="false" ValueChanged="@(OnSearch)" Value="@_searchString" Placeholder="@ConstantString.Search" Adornment="Adornment.End"
                                      AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small">
                        </MudTextField>
                    }
                </MudStack>
            </MudStack>
        </MudStack>
    </ToolBarContent>
    <Columns>
        <SelectColumn ShowInFooter="false"></SelectColumn>
        <TemplateColumn HeaderStyle="width:60px" Title="@ConstantString.Actions" Sortable="false">
            <CellTemplate>
                @if (_canEdit || _canDelete || _canManageRoles || _canRestPassword || _canSendRestPasswordMail || _canManagePermissions)
                {
                    <MudMenu Icon="@Icons.Material.Filled.Edit" Variant="Variant.Filled" Size="Size.Small"
                             Dense="true"
                             EndIcon="@Icons.Material.Filled.KeyboardArrowDown" IconColor="Color.Info" AnchorOrigin="Origin.CenterLeft">
                        @if (_canEdit)
                        {
                            <MudMenuItem OnClick="@(() => OnEdit(context.Item))">@ConstantString.Edit</MudMenuItem>
                        }
                        @if (_canDelete)
                        {
                            <MudMenuItem OnClick="@(() => OnDelete(context.Item))">@ConstantString.Delete</MudMenuItem>
                        }
                        @if (_canManagePermissions)
                        {
                            <MudMenuItem OnClick="@(() => OnSetPermissions(context.Item))">@L["Set Permissions"]</MudMenuItem>
                        }
                        @if (_canSendRestPasswordMail)
                        {
                            <MudMenuItem OnClick="@(() => OnSendResetPassword(context.Item))">@L["Send Reset Password Email"]</MudMenuItem>
                        }
                        @if (_canRestPassword)
                        {
                            <MudMenuItem Disabled="@(!context.Item.IsActive)" OnClick="@(() => OnResetPassword(context.Item))">@L["Reset Password"]</MudMenuItem>
                        }
                    </MudMenu>
                }
                else
                {
                    <MudButton Variant="Variant.Filled"
                               StartIcon="@Icons.Material.Filled.DoNotTouch" IconColor="Color.Secondary" Size="Size.Small"
                               Color="Color.Surface">
                        @ConstantString.NoAllowed
                    </MudButton>
                }
            </CellTemplate>
        </TemplateColumn>
        <PropertyColumn Property="x => x.Tenant" Title="@L[_currentDto.GetMemberDescription(x => x.Tenant)]">
            <CellTemplate>
                <div>
                    <MudText>@context.Item.Tenant?.Name</MudText>
                </div>
            </CellTemplate>
            <FooterTemplate>
                @L["Selected"]: @_selectedItems.Count
            </FooterTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.UserName" Title="@L[_currentDto.GetMemberDescription(x => x.UserName)]">
            <CellTemplate>
                <UserCard Item="@context.Item" OnSendVerify="@(x=> SendVerify(x))" DisplayOnlineStatus="@_canViewOnlineStatus"></UserCard>
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Email" Title="@L["Full Name"]">
            <CellTemplate>
                <div class="d-flex flex-column">
                    <MudText Typo="Typo.body2">@context.Item.DisplayName</MudText>
                </div>
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Superior" Title="@L[_currentDto.GetMemberDescription(x => x.Superior)]">
            <CellTemplate>
                @context.Item.Superior?.UserName
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Provider" Title="@L[_currentDto.GetMemberDescription(x => x.Provider)]" />

        <PropertyColumn Sortable="false" Property="x => x.AssignedRoles" Title="@L[_currentDto.GetMemberDescription(x => x.AssignedRoles)]">
            <CellTemplate>
                @if (context.Item.AssignedRoles is not null)
                {
                    foreach (var str in context.Item.AssignedRoles)
                    {
                        <MudChip Size="Size.Small" Variant="Variant.Text">@str</MudChip>
                    }
                }
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.IsActive" Title="@L[_currentDto.GetMemberDescription(x => x.IsActive)]">
            <CellTemplate>
                <div>
                    @if (!context.Item.IsActive || (context.Item.LockoutEnd is not null && context.Item.LockoutEnd > DateTime.UtcNow))
                    {
                        <MudTooltip Text="@L["Click to change status to active."]" Delay="300">
                            <MudSwitch T="bool" Value="context.Item.IsActive" ValueChanged="@(()=>SetActive(context.Item))" ThumbIcon="@Icons.Material.Filled.Close" ThumbIconColor="Color.Error">
                            </MudSwitch>
                        </MudTooltip>
                    }
                    else
                    {
                        <MudTooltip Text="@L["Click to change status to inactive."]" Delay="300">
                            <MudSwitch T="bool" Value="context.Item.IsActive" ValueChanged="@(()=>SetActive(context.Item))" ThumbIcon="@Icons.Material.Filled.Done" ThumbIconColor="Color.Success">
                            </MudSwitch>

                        </MudTooltip>
                    }
                </div>
            </CellTemplate>
        </PropertyColumn>
        <PropertyColumn Property="x => x.Created" Title="@L[_currentDto.GetMemberDescription(x => x.Created)]">
            <CellTemplate>
                <UtcToLocal UTCDateTime="@context.Item.Created"></UtcToLocal>
            </CellTemplate>
        </PropertyColumn>
    </Columns>

    <PagerContent>
        <MudDataGridPager PageSizeOptions="@(new[] { 10, 15, 30, 50, 100, 500, 1000 })" />
    </PagerContent>
</MudDataGrid>

<PermissionsDrawer OnAssignAllChanged="OnAssignAllChangedHandler" Waiting="@_processing" OnOpenChanged="OnOpenChangedHandler" Open="_showPermissionsDrawer" Permissions="_permissions" OnAssignChanged="OnAssignChangedHandler"></PermissionsDrawer>


@code {
    [CascadingParameter] private Task<AuthenticationState> AuthState { get; set; } = default!;
    [CascadingParameter] public UserProfile? UserProfile { get; set; }
    private UserManager<ApplicationUser> UserManager = null!;
    private RoleManager<ApplicationRole> RoleManager = null!;
    private PermissionHelper PermissionHelper = null!;
    private int _defaultPageSize = 15;
    private HashSet<ApplicationUserDto> _selectedItems = new();
    private readonly ApplicationUserDto _currentDto = new();
    private string _searchString = string.Empty;
    private string _selectedTenantId = " ";
    private string Title { get; set; } = "Users";
    private IList<PermissionModel> _permissions = new List<PermissionModel>();

    private TimeSpan RefreshInterval => TimeSpan.FromHours(2);


    private MudDataGrid<ApplicationUserDto> _table = null!;
    private bool _processing;
    private bool _showPermissionsDrawer;
    private bool _canCreate;
    private bool _canSearch;
    private bool _canEdit;
    private bool _canDelete;
    private bool _canManageRoles;
    private bool _canRestPassword;
    private bool _canSendRestPasswordMail;
    private bool _canManagePermissions;
    private bool _canImport;
    private bool _canExport;
    private bool _loading;
    private bool _exporting;
    private bool _uploading;
    private bool _canViewOnlineStatus;
    private List<string?> _roles = new();
    private string? _searchRole;

    protected override async Task OnInitializedAsync()
    {
        Title = L[_currentDto.GetClassDescription()];
        InitializeServices();
        var state = await AuthState;
        _canCreate = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Create)).Succeeded;
        _canSearch = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Search)).Succeeded;
        _canEdit = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Edit)).Succeeded;
        _canDelete = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Delete)).Succeeded;
        _canManageRoles = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.ManageRoles)).Succeeded;
        _canRestPassword = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.RestPassword)).Succeeded;
        _canSendRestPasswordMail = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.SendRestPasswordMail)).Succeeded;
        _canManagePermissions = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.ManagePermissions)).Succeeded;
        _canImport = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Import)).Succeeded;
        _canExport = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.Export)).Succeeded;
        _canViewOnlineStatus = (await AuthService.AuthorizeAsync(state.User, Permissions.Users.ViewOnlineStatus)).Succeeded;
        _roles = await RoleManager.Roles.Select(x => x.Name).ToListAsync();

        //var list= await  UserManager.Users.Include(x=>x.LastModifiedByUser).Include(x=>x.CreatedByUser).ToListAsync();
    }
    private void InitializeServices()
    {
        RoleManager = ScopedServices.GetRequiredService<RoleManager<ApplicationRole>>();
        UserManager = ScopedServices.GetRequiredService<UserManager<ApplicationUser>>();
        PermissionHelper = ScopedServices.GetRequiredService<PermissionHelper>();
    }
    private async Task OnInvalidated(IComputed state)
    {
        await InvokeAsync(StateHasChanged);
    }
    public void Dispose()
    {

    }


    private Expression<Func<ApplicationUser, bool>> CreateSearchPredicate()
    {
        return x =>
            (x.UserName!.Contains(_searchString) ||
             x.Email!.Contains(_searchString) ||
             x.DisplayName!.Contains(_searchString) ||
             x.PhoneNumber!.Contains(_searchString) ||
             x.Provider!.Contains(_searchString)) &&
            (_searchRole == null || (_searchRole != null && x.UserRoles.Any(x => x.Role.Name == _searchRole))) &&
            (_selectedTenantId == " " || (_selectedTenantId != " " && x.TenantId == _selectedTenantId));
    }
    private async Task<GridData<ApplicationUserDto>> ServerReload(GridState<ApplicationUserDto> state)
    {
        try
        {
            _loading = true;
            var searchPredicate = CreateSearchPredicate();
            var count =await  UserManager.Users.CountAsync(searchPredicate);
            var data = await UserManager.Users.Where(searchPredicate)
                    .Include(x => x.UserRoles).ThenInclude(ur => ur.Role)
                    .Include(x => x.CreatedByUser)
                    .Include(x => x.LastModifiedByUser)
                    .Include(x => x.Superior)
                    .EfOrderBySortDefinitions(state)
                    .Skip(state.Page * state.PageSize).Take(state.PageSize).ProjectTo().ToListAsync();
    
            return new GridData<ApplicationUserDto> { TotalItems = count, Items = data };
        }
        finally
        {
            _loading = false;
        }
    }
    private async Task OnChangedListView(string tenantId)
    {
        _selectedTenantId = tenantId;
        await _table.ReloadServerData();
    }
    private async Task OnSearch(string text)
    {
        if (_loading)
            return;
        _searchString = text.ToLower();
        await _table.ReloadServerData();
    }

    private async Task OnSearchRole(string role)
    {
        if (_loading)
            return;
        _searchRole = role;
        await _table.ReloadServerData();
    }

    private async Task OnRefresh()
    {
        await _table.ReloadServerData();
    }
    private async Task ShowCreateUserDialog(ApplicationUserDto model, string title)
    {
        var parameters = new DialogParameters<UserFormDialog>
        {
            { x => x.Model, model },
            { x => x.UserProfile, UserProfile }
        };
        var options = new DialogOptions { CloseButton = true, CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
        var dialog = await DialogService.ShowAsync<UserFormDialog>(title, parameters, options);
        var result = await dialog.Result;
        if (result is not null && !result.Canceled)
        {
            await ProcessUserCreation(model);
        }
    }

    private async Task ShowEditUserDialog(ApplicationUserDto model, string title)
    {
        var parameters = new DialogParameters<UserFormDialog>
        {
            { x => x.Model, model },
            { x => x.UserProfile, UserProfile }
        };
        var options = new DialogOptions { CloseButton = true, CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small, FullWidth = true };
        var dialog = await DialogService.ShowAsync<UserFormDialog>(title, parameters, options);
        var result = await dialog.Result;
        if (result is not null && !result.Canceled)
        {
            await ProcessUserUpdate(model);
        }
    }
    private async Task ProcessUserCreation(ApplicationUserDto model)
    {
        var applicationUser = new ApplicationUser
            {
                Provider = model.Provider,
                DisplayName = model.DisplayName,
                UserName = model.UserName,
                TenantId = model.TenantId,
                Email = model.Email,
                PhoneNumber = model.PhoneNumber,
                SuperiorId = model.Superior?.Id,
                ProfilePictureDataUrl = model.ProfilePictureDataUrl,
                EmailConfirmed = false,
                IsActive = model.IsActive,
                LanguageCode = model.LanguageCode,
                TimeZoneId = model.TimeZoneId
            };

        var identityResult = await UserManager.CreateAsync(applicationUser);
        if (!identityResult.Succeeded)
        {
            Snackbar.Add($"{string.Join(",", identityResult.Errors.Select(x => x.Description).ToArray())}", Severity.Error);
            return;
        }

        Snackbar.Add($"{L["New user created successfully."]}", Severity.Info);
        await AssignRolesToUser(applicationUser, model.AssignedRoles);
        Logger.LogInformation("Create a user succeeded. Username: {@UserName:l}, UserId: {@UserId}", applicationUser.UserName, applicationUser.Id);
        UserService.Refresh();
        await OnRefresh();
    }
    private async Task ProcessUserUpdate(ApplicationUserDto item)
    {
        var user = await UserManager.FindByIdAsync(item.Id!) ?? throw new NotFoundException($"The application user [{item.Id}] was not found.");
        var roles = await UserManager.GetRolesAsync(user);
        if (roles.Count > 0)
        {
            await UserManager.RemoveFromRolesAsync(user, roles);
        }

        user.Email = item.Email;
        user.PhoneNumber = item.PhoneNumber;
        user.ProfilePictureDataUrl = item.ProfilePictureDataUrl;
        user.DisplayName = item.DisplayName;
        user.Provider = item.Provider;
        user.UserName = item.UserName;
        user.IsActive = item.IsActive;
        user.TenantId = item.TenantId;
        user.SuperiorId = item.Superior?.Id;
        user.LanguageCode = item.LanguageCode;
        user.TimeZoneId = item.TimeZoneId;
        var identityResult = await UserManager.UpdateAsync(user);
        if (identityResult.Succeeded)
        {
            if (item.AssignedRoles is not null && item.AssignedRoles.Length > 0)
            {
                await UserManager.AddToRolesAsync(user, item.AssignedRoles);
            }

            Snackbar.Add($"{L["The user updated successfully."]}", Severity.Info);
            await OnRefresh();
            UserService.Refresh();
            await OnlineUserTracker.Update(item.Id,
                                           item.UserName,
                                           item.DisplayName ?? string.Empty,
                                           item.ProfilePictureDataUrl ?? string.Empty);
        }
        else
        {
            Snackbar.Add($"{string.Join(",", identityResult.Errors.Select(x => x.Description).ToArray())}", Severity.Error);
        }
    }
    private async Task AssignRolesToUser(ApplicationUser user, string[]? roles)
    {
        if (roles is not null && roles.Length > 0)
        {
            await UserManager.AddToRolesAsync(user, roles);
        }
    }
    private async Task OnCreate()
    {
        var model = new ApplicationUserDto
            {
                Provider = "Local",
                Email = "",
                UserName = "",
                AssignedRoles = Array.Empty<string>(),
                TimeZoneId = TimeZoneInfo.Local.Id,
                LanguageCode = CultureInfo.CurrentCulture.Name
            };
        await ShowCreateUserDialog(model, L["Create a new user"]);
    }

    private async Task OnEdit(ApplicationUserDto item)
    {
        await ShowEditUserDialog(item, L["Edit the user"]);
    }
    private async Task ShowDeleteConfirmationDialogAsync(ApplicationUserDto dto)
    {
        var contentText = string.Format(ConstantString.DeleteConfirmation, dto.UserName);
        await DialogServiceHelper.ShowConfirmationDialogAsync(ConstantString.DeleteConfirmationTitle, contentText, async () =>
        {
            await InvokeAsync(async () =>
            {
                await _table.ReloadServerData();
                _selectedItems.Clear();
            });
        });
    }
    private async Task ProcessUserDeletion(ApplicationUserDto dto)
    {
        var user = await UserManager.FindByIdAsync(dto.Id) ?? throw new NotFoundException($"The application user [{dto.Id}] was not found.");
        var identityResult = await UserManager.DeleteAsync(user);
        if (identityResult.Succeeded)
        {
            Logger.LogInformation("Delete the user succeeded. Username: {@UserName:l}, UserId: {@UserId}", user.UserName, user.Id);
            Snackbar.Add($"{ConstantString.DeleteSuccess}", Severity.Info);
            await OnRefresh();
            UserService.Refresh();
        }
        else
        {
            Snackbar.Add($"{string.Join(",", identityResult.Errors.Select(x => x.Description).ToArray())}", Severity.Error);
        }
    }
    private async Task OnDelete(ApplicationUserDto dto)
    {
        var state = await AuthState;
        if (dto.Id == state.User.GetUserId())
        {
            Snackbar.Add(L["You cannot delete your own account!"], Severity.Error);
            return;
        }
        await ShowDeleteConfirmationDialogAsync(dto);

    }

    private async Task OnDeleteChecked()
    {
        var state = await AuthState;
        var currentUserId = state.User.GetUserId();
        if (_selectedItems.Any(x => x.Id == currentUserId))
        {
            Snackbar.Add(L["You cannot delete your own account!"], Severity.Error);
            return;
        }

        var deleteContent = string.Format(ConstantString.DeleteConfirmation, _selectedItems.Count);
        await DialogServiceHelper.ShowConfirmationDialogAsync(ConstantString.DeleteConfirmationTitle, deleteContent, async () =>
        {
            await InvokeAsync(async () =>
            {
                await ProcessSelectedUsersDeletion();
            });
        });
    }
    private async Task ProcessSelectedUsersDeletion()
    {
        var deleteIds = _selectedItems.Select(x => x.Id).ToArray();
        var deleteUsers = await UserManager.Users.Where(x => deleteIds.Contains(x.Id)).ToListAsync();

        foreach (var deleteUser in deleteUsers)
        {
            var deleteResult = await UserManager.DeleteAsync(deleteUser);
            if (!deleteResult.Succeeded)
            {
                Snackbar.Add($"{string.Join(",", deleteResult.Errors.Select(x => x.Description).ToArray())}", Severity.Error);
                return;
            }

            Logger.LogInformation("Delete the user succeeded. Username: {@UserName:l}, UserId: {@UserId}", deleteUser.UserName, deleteUser.Id);
        }

        Snackbar.Add($"{ConstantString.DeleteSuccess}", Severity.Info);
        await OnRefresh();
        UserService.Refresh();
    }
    private async Task SendVerify(ApplicationUserDto item)
    {
        var user = await UserManager.FindByIdAsync(item.Id) ?? throw new NotFoundException($"The application user [{item.Id}] was not found.");
        var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
        var callbackUrl = Navigation.GetUriWithQueryParameters(
            Navigation.ToAbsoluteUri(ConfirmEmail.PageUrl).AbsoluteUri,
            new Dictionary<string, object?> { ["userId"] = item.Id, ["code"] = code, ["returnUrl"] = "/" });
        await Mediator.Publish(new UserActivationNotification(callbackUrl, item.Email, item.Id, item.UserName));
        Snackbar.Add(string.Format(L["Verification email sent to {0}."], item.Email), Severity.Info);
        Logger.LogInformation($"Verification email sent to user {item.UserName} ({item.Id}) with callback URL {callbackUrl}");
    }
    #region change user status to active
    private async Task SetActive(ApplicationUserDto item)
    {
        var user = await UserManager.FindByIdAsync(item.Id!) ?? throw new NotFoundException($"Application user not found {item.Id}.");
        await ToggleUserActiveState(user, item);
    }

    private async Task ToggleUserActiveState(ApplicationUser user, ApplicationUserDto item)
    {
        if (user.IsActive)
        {
            await DeactivateUser(user, item);
        }
        else
        {
            await ActivateUser(user, item);
        }
    }
    private async Task ActivateUser(ApplicationUser user, ApplicationUserDto item)
    {
        user.IsActive = true;
        user.LockoutEnd = null;
        var identityResult = await UserManager.UpdateAsync(user);
        if (identityResult.Succeeded)
        {
            item.IsActive = true;
            item.LockoutEnd = null;
            Snackbar.Add($"{L["The user has been activated."]}", Severity.Info);
        }
        else
        {
            Snackbar.Add($"{string.Join(",", identityResult.Errors.Select(x => x.Description).ToArray())}", Severity.Error);
        }
    }

    private async Task DeactivateUser(ApplicationUser user, ApplicationUserDto item)
    {
        user.IsActive = false;
        user.LockoutEnd = DateTimeOffset.MaxValue;
        var identityResult = await UserManager.UpdateAsync(user);

        if (identityResult.Succeeded)
        {
            item.IsActive = false;
            item.LockoutEnd = DateTimeOffset.MaxValue;
            Snackbar.Add($"{L["The user has been inactivated."]}", Severity.Info);
        }
        else
        {
            Snackbar.Add($"{string.Join(",", identityResult.Errors.Select(x => x.Description).ToArray())}", Severity.Error);
        }
    }
    #endregion
    private async Task OnSendResetPassword(ApplicationUserDto item)
    {
        var user = await UserManager.FindByIdAsync(item.Id) ?? throw new NotFoundException($"The application user [{item.Id}] was not found.");
        var code = await UserManager.GeneratePasswordResetTokenAsync(user);
        code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
        var callbackUrl = Navigation.GetUriWithQueryParameters(
            Navigation.ToAbsoluteUri(ResetPassword.PageUrl).AbsoluteUri,
            new Dictionary<string, object?> { ["userId"] = user.Id, ["token"] = code });
        await Mediator.Publish(new ResetPasswordNotification(callbackUrl, item.Email, item.UserName));
        Snackbar.Add(string.Format(L["A new password for {0} has been sent via email. The user will be required to enter a new password upon initial login."], item.UserName), Severity.Info);
    }
    private async Task OnResetPassword(ApplicationUserDto item)
    {
        var model = new ResetPasswordFormModel { Id = item.Id, DisplayName = item.DisplayName, UserName = item.UserName, ProfilePictureDataUrl = item.ProfilePictureDataUrl };
        var parameters = new DialogParameters<ResetPasswordDialog>
        {
            { x => x.Model, model }
        };
        var options = new DialogOptions { CloseOnEscapeKey = true, CloseButton = true, MaxWidth = MaxWidth.ExtraSmall };
        var dialog = await DialogService.ShowAsync<ResetPasswordDialog>(L["Set Password"], parameters, options);
        var result = await dialog.Result;
        if (result is not null && !result.Canceled)
        {
            await ProcessPasswordReset(item, model);
        }
    }
    private async Task ProcessPasswordReset(ApplicationUserDto item, ResetPasswordFormModel model)
    {

        var user = await UserManager.FindByIdAsync(item.Id!) ?? throw new NotFoundException($"Application user not found {item.Id}.");
        var token = await UserManager.GeneratePasswordResetTokenAsync(user);
        var identityResult = await UserManager.ResetPasswordAsync(user, token, model.Password!);

        if (identityResult.Succeeded)
        {
            if (!user.EmailConfirmed)
            {
                user.EmailConfirmed = true;
                await UserManager.UpdateAsync(user);
            }

            Snackbar.Add($"{L["Reset password successfully."]}", Severity.Info);
        }
        else
        {
            Snackbar.Add($"{string.Join(",", identityResult.Errors.Select(x => x.Description).ToArray())}", Severity.Error);
        }
    }
    private async Task OnSetPermissions(ApplicationUserDto item)
    {
        _showPermissionsDrawer = true;
        _permissions = await PermissionHelper.GetAllPermissionsByUserId(item.Id);
    }

    private Task OnOpenChangedHandler(bool state)
    {
        _showPermissionsDrawer = state;
        return Task.CompletedTask;
    }



    private async Task OnAssignAllChangedHandler(List<PermissionModel> models)
    {
        try
        {
            _processing = true;
            var userId = models.First().UserId;
            var user = await UserManager.FindByIdAsync(userId!) ?? throw new NotFoundException($"not found application user: {userId}");

            foreach (var model in models)
            {
                await ProcessPermissionChange(user, model);
            }

            Snackbar.Add($"{L["Authorization has been changed."]}", Severity.Info);
            await ClearClaimsCache(user.Id);
        }
        finally
        {
            _processing = false;
        }
    }

    private async Task ProcessPermissionChange(ApplicationUser user, PermissionModel model)
    {
        if (model.Assigned)
        {
            if (model.ClaimType is not null && model.ClaimValue is not null)
            {
                await UserManager.AddClaimAsync(user, new Claim(model.ClaimType, model.ClaimValue));
            }
        }
        else
        {
            var removed = (await UserManager.GetClaimsAsync(user)).FirstOrDefault(x => x.Value == model.ClaimValue);
            if (removed is not null)
            {
                await UserManager.RemoveClaimAsync(user, removed);
            }
        }
    }

    private async Task ClearClaimsCache(string userId)
    {
        var key = $"get-claims-by-{userId}";
        FusionCache.Remove(key);
        await Task.Delay(300);
    }

    private async Task OnAssignChangedHandler(PermissionModel model)
    {
        try
        {
            _processing = true;
            var userId = model.UserId!;
            var user = await UserManager.FindByIdAsync(userId) ?? throw new NotFoundException($"Application user Not Found {userId}.");

            model.Assigned = !model.Assigned;
            if (model.Assigned)
            {
                if (model.ClaimType is not null && model.ClaimValue is not null)
                {
                    await UserManager.AddClaimAsync(user, new Claim(model.ClaimType, model.ClaimValue));
                    Snackbar.Add($"{L["Permission assigned successfully."]}", Severity.Info);
                }
            }
            else
            {
                var removed = (await UserManager.GetClaimsAsync(user)).FirstOrDefault(x => x.Value == model.ClaimValue);
                if (removed is not null)
                {
                    await UserManager.RemoveClaimAsync(user, removed);
                }

                Snackbar.Add($"{L["Permission removed successfully."]}", Severity.Info);
            }

            await ClearClaimsCache(user.Id);
        }
        finally
        {
            _processing = false;
        }
    }

    private async Task OnExport()
    {
        _exporting = true;
        try
        {
            var searchPredicate = CreateSearchPredicate();

            var items = await UserManager.Users.Where(searchPredicate)
                .Select(x => new ApplicationUserDto
                    {
                        Id = x.Id,
                        UserName = x.UserName!,
                        DisplayName = x.DisplayName,
                        Email = x.Email!,
                        PhoneNumber = x.PhoneNumber,
                        SuperiorId = x.SuperiorId,
                        TenantId = x.TenantId
                    }).ToListAsync();

            var result = await ExportUsers(items);
            await BlazorDownloadFileService.DownloadFile($"{L["Users"]}.xlsx", result, "application/octet-stream");
            Snackbar.Add($"{ConstantString.ExportSuccess}", Severity.Info);
        }
        finally
        {
            _exporting = false;
        }
    }

    private async Task<byte[]> ExportUsers(List<ApplicationUserDto> items)
    {
        return await ExcelService.ExportAsync(items,
            new Dictionary<string, Func<ApplicationUserDto, object?>>
                                                        {
                { L["Id"], item => item.Id },
                { L["User Name"], item => item.UserName },
                { L["Full Name"], item => item.DisplayName },
                { L["Email"], item => item.Email },
                { L["Phone Number"], item => item.PhoneNumber },
                { L["Tenant Id"], item => item.TenantId },
                { L["Superior Id"], item => item.SuperiorId }
                                                        }, L["Users"]);
    }

    private async Task OnImportData(IBrowserFile file)
    {
        _uploading = true;
        try
        {
            var stream = new MemoryStream();
            await file.OpenReadStream(GlobalVariable.MaxAllowedSize).CopyToAsync(stream);
            var result = await ImportUsers(stream);

            if (result?.Succeeded == true)
            {
                if (result.Data != null)
                {
                    await ProcessImportedUsers(result.Data);
                    await _table.ReloadServerData();
                }
                Snackbar.Add($"{ConstantString.ImportSuccess}", Severity.Info);
            }
            else if (result?.Errors != null)
            {
                foreach (var msg in result.Errors)
                {
                    Snackbar.Add($"{msg}", Severity.Error);
                }
            }
        }
        finally
        {
            _uploading = false;
        }

    }

    private async Task<IResult<IEnumerable<ApplicationUser>>> ImportUsers(MemoryStream stream)
    {
        return await ExcelService.ImportAsync(stream.ToArray(), new Dictionary<string, Func<DataRow, ApplicationUser, object?>>
        {
            { L["User Name"], (row, item) => item.UserName = row[L["User Name"]]?.ToString() },
            { L["Full Name"], (row, item) => item.DisplayName = row[L["Full Name"]]?.ToString() },
            { L["Email"], (row, item) => item.Email = row[L["Email"]]?.ToString() },
            { L["Phone Number"], (row, item) => item.PhoneNumber = row[L["Phone Number"]]?.ToString() },
            { L["Tenant Id"], (row, item) => item.TenantId = row[L["Tenant Id"]]?.ToString() },
            { L["Superior Id"], (row, item) => item.SuperiorId = row[L["Superior Id"]]?.ToString() }
        }, L["Users"]);
    }

    private async Task ProcessImportedUsers(IEnumerable<ApplicationUser> users)
    {
        foreach (var user in users)
        {
            if (!UserManager.Users.Any(x => x.UserName == user.UserName))
            {
                var identityResult = await UserManager.CreateAsync(user);
                if (identityResult.Succeeded)
                {
                    await UserManager.AddToRolesAsync(user, new[] { RoleName.Basic });
                }
                else
                {
                    Snackbar.Add($"{string.Join(',', identityResult.Errors.Select(x => x.Description))}", Severity.Error);
                }
            }
        }
    }

    private async Task SendWelcomeNotification(string toEmail, string userName)
    {
        var callbackUrl = Navigation.GetUriWithQueryParameters(
            Navigation.ToAbsoluteUri(Login.PageUrl).AbsoluteUri,
            new Dictionary<string, object?> { ["returnUrl"] = "/" });

        await Mediator.Publish(new SendWelcomeNotification(callbackUrl, toEmail, userName));
        Logger.LogInformation("{UserName} Activated Successfully!", toEmail);
    }



}